﻿using System.Threading.Tasks.Dataflow;
using K9Nano.Dependency;
using K9Nano.Domain.Caching;
using K9Nano.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace K9Nano.Domain.Logging;

internal class ApiLogManager : IApiLogManager, ISingletonDependency
{
    const int BufferSize = 100;

    private readonly ILogger _logger;
    private readonly BatchBlock<ApiLogEntity> _buffer;
    private readonly ActionBlock<ApiLogEntity[]> _saveAction;
    private readonly IServiceProvider _serviceProvider;
    private readonly Timer _trigger;

    public ApiLogManager(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _logger = serviceProvider.GetRequiredService<ILogger<ApiLogManager>>();
        _buffer = new(BufferSize);
        _saveAction = new(BulkSave);
        _buffer.LinkTo(_saveAction);
        _buffer.Completion.ContinueWith(delegate { _saveAction.Complete(); });

        _trigger = new Timer(_ =>
        {
            try
            {
                _buffer.TriggerBatch();
            }
            catch
            {
                // ignored
            }
        }, null, 10_000, 20_000);
    }

    public void Dispose()
    {
        _trigger.Dispose();
        _buffer.Complete();
        _saveAction.Completion.Wait();
    }

    public void Emit(ApiLogEntity log)
    {
        _buffer.Post(log);
    }

    private void BulkSave(ApiLogEntity[] entities)
    {
        try
        {
            using var scope = _serviceProvider.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<LogDbContext>();
            var userCache = scope.ServiceProvider.GetRequiredService<IUserCache>();
            for (var i = 0; i < entities.Length; i++)
            {
                var userId = entities[i].UserId;
                if (userId == null) continue;
                entities[i].UserName = userCache.GetName(userId.Value);
            }
            db.BulkInsert(entities);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "保存HTTP请求日志失败");
        }
    }
}